/*************************************************************************
 * @file: geomchecker.cpp
 * @date: 01/04/2012
 * TOPOL v1_0 - (c)copyright 2012 TEC/UFF
 *************************************************************************
 * This is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 * This software is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 ************************************************************************/

// includes
//----------
#include "geomchecker.h"
#include "hedmodel.h"
#include "entity/vertex.h"
#include "entity/edge.h"
#include "entity/ipoint.h"
#include <iostream>
#include <math.h>



///////////////////////////////////////////////////
///  Constructor and Destructor Implementation  ///
///////////////////////////////////////////////////

//-------------------------------------------------------------------------
GeomChecker::GeomChecker()
{

}
//-------------------------------------------------------------------------



///////////////////////////////////////
///  PUBLIC Methods Implementation  ///
///////////////////////////////////////

//-------------------------------------------------------------------------
bool 
GeomChecker::hasAnyVertex( HEDModel& _model )
{
  int numVtx = _model.getNumberOfVertexes();
  if( numVtx > 0 )
    return true;
  return false;
}
//-------------------------------------------------------------------------

//-------------------------------------------------------------------------
bool 
GeomChecker::hasVertexWithGivenCoords( HEDModel& _model, int& _id,
                                       double& _x, double& _y, double& _z, double _tol )
{
  Vertex* v = _model.getVertex(_x,_y,_z,_tol);
  if( v != NULL )
  {
    _id = v->getVertexno();
    _x = v->getPoint()->getX();
    _y = v->getPoint()->getY();
    _z = v->getPoint()->getZ();
    return true;
  }
  return false;
}
//-------------------------------------------------------------------------

//-------------------------------------------------------------------------
bool 
GeomChecker::intersectsVertex( HEDModel& _model, double* _curvePts, int& _n, double* _intersPts )
{
  vector<Vertex*> vertices = _model.getVertexes();
  for( size_t i=0; i< vertices.size(); i++ )
  {
    IPoint* pt = vertices[i]->getPoint();
    //double LineMag2 = _LineStart.distance2(_LineEnd);
    //if (LineMag2 == 0.0)
    //  return 0;
    //Point3D v1 = _Point   - _LineStart;
    //Point3D v2 = _LineEnd - _LineStart;
    //double U = (v1.dotProd (v2)) / LineMag2;
    //if( U < 0.0 || U > 1.0 )
    //  return 0; // closest point does not fall within the line segment
    //_Intersection = _LineStart + U * v2;
    //_d = _Point.distance(_Intersection);
  }
  return false;
}
//-------------------------------------------------------------------------

//-------------------------------------------------------------------------
bool 
GeomChecker::intersectsCurves( HEDModel& _model, IPoint& _cp1, IPoint& _cp2,
                               list<IPoint>& _intersPts, list<Edge*>& _iEdges )
{
  double xi, yi;
  vector<IPoint> vInPts;
  vector<Edge*> vEdges;
  vector<Edge*> edges = _model.getEdges();
  for( size_t i=0; i< edges.size(); i++ )
  {
    IPoint* pt1 = edges[i]->getFirstVertex()->getPoint();
    IPoint* pt2 = edges[i]->getSeconVertex()->getPoint();
    int inters = this->computeLineLineIntersection( pt1->getX(), pt1->getY(), pt2->getX(), pt2->getY(),
                                                    _cp1.getX(), _cp1.getY(), _cp2.getX(), _cp2.getY(), xi, yi );
    if( inters )
    {
      IPoint interPt(xi,yi,0.0);
      vInPts.push_back( interPt );
      vEdges.push_back( edges[i] );
    }
  }
  // sorts the intersected points based on the distances to the curve first point
  int i = vInPts.size();
  if( i > 0 )
  {
    bool ordered = false;
    int k = i;
    while( (ordered == false) && (k > 1) )
    {
      ordered = true;
      int j = -1;
      int l = 0;
      while( l < i-1 )
      {
        j++;
        double distj = _cp1.distance2( vInPts[j].getX(),vInPts[j].getY(),vInPts[j].getZ() );
        l++;
        double distm = _cp1.distance2( vInPts[l].getX(),vInPts[l].getY(),vInPts[l].getZ() );
        if( distj > distm )
        {
          IPoint pt = vInPts[j];
          vInPts[j] = vInPts[l];
          vInPts[l] = pt;
          Edge* ed = vEdges[j];
          vEdges[j] = vEdges[l];
          vEdges[l] = ed;
          ordered = false;
        }
        k++;
      }
    }
    // stores the data from vectors in the lists
    for( size_t i=0; i< vInPts.size(); i++ )
    {
      _intersPts.push_back( vInPts[i] );
      _iEdges.push_back( vEdges[i] );
    }
    return true;
  }
  return false;
}
//-------------------------------------------------------------------------

//-------------------------------------------------------------------------
bool 
GeomChecker::liesOnCurve( HEDModel& _model, int& _id,
                          double& _x, double& _y, double& _z, double& _tol )
{
  Edge* edg = _model.getEdge(_x,_y,_z,_tol);
  if( edg != NULL )
  {
    IPoint* pt1 = edg->getFirstVertex()->getPoint();
    IPoint* pt2 = edg->getSeconVertex()->getPoint();
    double vx = pt2->getX() - pt1->getX();
    double vy = pt2->getY() - pt1->getY();
    double t = (vx*(_x-pt1->getX()) + vy*(_y-pt1->getY())) / (vx*vx + vy*vy);
    _x = pt1->getX() + t * vx;
    _y = pt1->getY() + t * vy;
    //id = edg->getEdgeno();
    return true;
  }
  return false;
}
//-------------------------------------------------------------------------



////////////////////////////////////////
///  PRIVATE Methods Implementation  ///
////////////////////////////////////////

//-------------------------------------------------------------------------
int 
GeomChecker::computeLineLineIntersection( double _x1, double _y1, double _x2, double _y2,
                                          double _x3, double _y3, double _x4, double _y4,
                                          double& _xi, double& _yi )
{
  double xD1,yD1,xD2,yD2,xD3,yD3;  
  double dot,deg,len1,len2;  
  double segmentLen1,segmentLen2;  
  double ua,ub,div;  
  // calculate differences  
  xD1=_x2-_x1;  
  xD2=_x4-_x3;  
  yD1=_y2-_y1;  
  yD2=_y4-_y3;  
  xD3=_x1-_x3;  
  yD3=_y1-_y3;    
  // calculate the lengths of the two lines  
  len1=sqrt(xD1*xD1+yD1*yD1);  
  len2=sqrt(xD2*xD2+yD2*yD2);  
  // calculate angle between the two lines.  
  dot=(xD1*xD2+yD1*yD2); // dot product  
  deg=dot/(len1*len2);  
  // if abs(angle)==1 then the lines are parallel,  
  // so no intersection is possible  
  if(fabs(deg)==1) return 0;
  // find intersection Pt between two lines  
  div=yD2*xD1-xD2*yD1;  
  ua=(xD2*yD3-yD2*xD3)/div;  
  ub=(xD1*yD3-yD1*xD3)/div;  
  double x=_x1+ua*xD1;  
  double y=_y1+ua*yD1;  
  // calculate the combined length of the two segments  
  // between Pt-p1 and Pt-p2  
  xD1=x-_x1;  
  xD2=x-_x2;  
  yD1=y-_y1;  
  yD2=y-_y2;  
  segmentLen1=sqrt(xD1*xD1+yD1*yD1)+sqrt(xD2*xD2+yD2*yD2);  
  // calculate the combined length of the two segments  
  // between Pt-p3 and Pt-p4  
  xD1=x-_x3;  
  xD2=x-_x4;  
  yD1=y-_y3;  
  yD2=y-_y4;  
  segmentLen2=sqrt(xD1*xD1+yD1*yD1)+sqrt(xD2*xD2+yD2*yD2);  
  // if the lengths of both sets of segments are the same as  
  // the lengths of the two lines the point is actually  
  // on the line segment.  
  // if the point isn't on the line, return null  
  if(fabs(len1-segmentLen1)>0.01 || fabs(len2-segmentLen2)>0.01)
    return 0;  
  // return the valid intersection  
  _xi = x;
  _yi = y;
  return 1;
}
//-------------------------------------------------------------------------

//-------------------------------------------------------------------------
int 
GeomChecker::comp( double _a, double _b, double _tol )
{
  double delta = fabs( _a - _b );
  if( delta < _tol )  return 0;
  else if( _a > _b )  return 1;
  else                return -1;
}
//-------------------------------------------------------------------------

//-------------------------------------------------------------------------
int 
GeomChecker::contvv( double _x1, double _y1, double _x2, double _y2, double _tol )
{
  double dx = _x1 - _x2;
  double dy = _y1 - _y2;
  double diff = dx * dx + dy * dy;
  return ( this->comp( diff, 0.0, _tol*_tol ) );
}
//-------------------------------------------------------------------------
